#include"PageCache.h"


PageCache PageCache::_sInst;


// 获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
    assert(k > 0 && k < NPAGES);

    // 先判断k号桶是否为空
    if(!_spanLists[k].Empty())
    {
        Span* kSpan = _spanLists[k].PopFront();

        // 每个页号都要和span建立映射
        for(size_t i = 0; i < kSpan->_n; i++)
        {
            _idSpanMap[kSpan->_pageId + i] = kSpan;
        }
    }

    // 遍历k+1号桶到最后
    for(size_t i = k + 1; i < NPAGES; i++)
    {
        if(!_spanLists[i].Empty())
        {
            Span* kSpan = new Span;
            Span* nSpan = _spanLists[i].PopFront();

            kSpan->_pageId = nSpan->_pageId;
            kSpan->_n = k;

            nSpan->_pageId += k;
            nSpan->_n -= k;

            // nSpan建立首尾页的页号与其映射
            _idSpanMap[nSpan->_pageId] = nSpan;
            _idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;

            // kSpan每个页号都要建立与kSpan的映射
            for(size_t i = 0; i < kSpan->_n; i++)
            {
                _idSpanMap[kSpan->_pageId + i] = kSpan;
            }

            _spanLists[nSpan->_n].PushFront(nSpan);

            return kSpan;
        }
    }

    // 走到这说明需要向系统堆申请大块span
    Span* bigSpan = new Span;
    void* ptr = SystemAlloc(NPAGES - 1);
    bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
    bigSpan->_n = NPAGES - 1;

    _spanLists[bigSpan->_n].PushFront(bigSpan);

    return NewSpan(k);
}


// 建立内存对象到span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{
    assert(obj);

    PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;

    auto ret = _idSpanMap.find(id);
    if(ret == _idSpanMap.end())
    {
        assert(false);
        return nullptr;
    }
    else
    {
        return ret->second;
    }
}


// 释放空闲的span, 并尝试做前后页span的合并
void PageCache::ReleaseSpanToPageCache(Span* span)
{
    assert(span);

    // 做前后页span的合并, 分为向前合并和向后合并

    // 向前合并
    while(1)
    {
        // 1. 前一页不存在, 不合并了
        PAGE_ID prevId = span->_pageId - 1;
        auto ret = _idSpanMap.find(prevId);
        if(ret == _idSpanMap.end())
        {
            break;
        }

        // 2. 前一页正在被使用, 不合并了
        Span* prevSpan = ret->second;
        if(prevSpan->_isUse == true)
        {
            break;
        }

        // 3. 合并出大于128页的span, 不合并了
        if(span->_n + prevSpan->_n > NPAGES - 1)
        {
            break;
        }

        span->_pageId = prevSpan->_pageId;
        span->_n += prevSpan->_n;

        _spanLists[prevSpan->_n].Erase(prevSpan);
        delete prevSpan;
    }

    // 向后合并
    while(1)
    {
        // 1. 后一页的span不存在, 不合并了
        PAGE_ID nextId = span->_pageId + span->_n;
        auto ret = _idSpanMap.find(nextId);
        if(ret == _idSpanMap.end())
        {
            break;
        }

        // 2. 后一页的span正在被使用, 不合并了
        Span* nextSpan = ret->second;
        if(nextSpan->_isUse == true)
        {
            break;
        }

        // 3. 合并出大于128页的span没办法管理, 不合并了
        if(span->_n + nextSpan->_n > NPAGES - 1)
        {
            break;
        }

        span->_n += nextSpan->_n;

        _spanLists[nextSpan->_n].Erase(nextSpan);
        delete nextSpan;
    }

    _spanLists[span->_n].PushFront(span);
    span->_isUse = false;

    _idSpanMap[span->_pageId] = span;
    _idSpanMap[span->_pageId + span->_n - 1] = span;
}